package service

import (
	"Gin/app/model"
	"Gin/utils/Redis"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"strconv"
	"sync"
	"time"
)

var upgrade = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

var MapLock sync.RWMutex
var OnlineMap = make(map[int]*model.OnLineUser)

var GReadChannel = make(chan *model.Message)
var GWriteChannel = make(chan *model.Message)

func GetConn(c *gin.Context) {
	coon, _ := upgrade.Upgrade(c.Writer, c.Request, nil)
	id, _ := strconv.Atoi(c.Query("id"))
	username := c.Query("username")
	client := &model.OnLineUser{
		UserId:       id,
		UserName:     username,
		Coon:         coon,
		ReadChannel:  make(chan model.SendMessage),
		WriteChannel: make(chan model.SendMessage),
	}
	MapLock.RLock()
	OnlineMap[client.UserId] = client
	MapLock.RUnlock()
	go client.Read()
	go client.Write()
	Run(client)
	coon.SetCloseHandler(func(code int, text string) error {
		fmt.Println("chat exit")
		return nil
	})
}

func Run(client *model.OnLineUser) {
	go GRead()
	go GWrite()
	for {
		_, msg, err := client.Coon.ReadMessage()
		if err != nil {
			log.Println("coon close")
			MapLock.RLock()
			delete(OnlineMap, client.UserId)
			MapLock.RUnlock()
			close(client.ReadChannel)
			close(client.WriteChannel)
			return
		}
		message := &model.Message{}
		err = json.Unmarshal(msg, message)
		if err != nil {
			log.Println("json Unmarshal err,", err.Error())
			continue
		}
		GReadChannel <- message
	}
}

func GRead() {
	for {
		select {
		case msg, ok := <-GReadChannel:
			if !ok {
				log.Println("ReadService err...")
				return
			}
			GWriteChannel <- msg
		}
	}
}

func GWrite() {
	cli := Redis.GetClient()
	for {
		select {
		case msg, ok := <-GWriteChannel:
			if !ok {
				log.Println("WriteService err...")
				return
			}
			OnlineUser := OnlineMap[msg.TargetId]
			sendMsg := model.SendMessage{
				Id:         2,
				Tick:       msg.FromName,
				TargetName: msg.TargetName,
				Content:    msg.Content,
				Icon:       msg.Icon,
				From:       msg.FromId,
				Target:     msg.TargetId,
			}
			if OnlineUser == nil {
				cli.RPush(cli.Context(), fmt.Sprintf(Redis.UserMQList, msg.TargetId, msg.FromId), sendMsg)
				cli.HIncrBy(cli.Context(), fmt.Sprintf(Redis.UserUnReadMessage, msg.TargetId), strconv.Itoa(msg.FromId), 1)
				sendMsg.Id = 1
				OnlineMap[msg.FromId].ReadChannel <- sendMsg
				addMessage(cli, fmt.Sprintf(Redis.UserMessageList, msg.FromId, msg.TargetId), sendMsg)
				saveLastMessage(msg.Content, msg.FromId, msg.TargetId, cli)
				continue
			}
			OnlineUser.ReadChannel <- sendMsg
			addMessage(cli, fmt.Sprintf(Redis.UserMessageList, OnlineUser.UserId, msg.FromId), sendMsg)
			sendMsg.Id = 1
			OnlineMap[msg.FromId].ReadChannel <- sendMsg
			addMessage(cli, fmt.Sprintf(Redis.UserMessageList, msg.FromId, msg.TargetId), sendMsg)
			saveLastMessage(msg.Content, msg.FromId, msg.TargetId, cli)
		}
	}
}

func addMessage(cli *redis.Client, key string, value interface{}) {
	cli.RPush(cli.Context(), key, value)
	cli.Expire(cli.Context(), key, Redis.UserMessageTimeExpire)
}

func saveLastMessage(content string, fromId, targetId int, cli *redis.Client) {
	last := &model.ChatMessage{
		Id:      0,
		Tick:    "",
		Icon:    "",
		Unread:  0,
		Content: content,
		Time:    time.Now().Format("2006-01-02 15:04"),
	}
	cli.HSet(cli.Context(), fmt.Sprintf(Redis.UserLastMessage, targetId), strconv.Itoa(fromId), last)
	cli.HSet(cli.Context(), fmt.Sprintf(Redis.UserLastMessage, fromId), strconv.Itoa(targetId), last)
}
